import { EventListener } from "./EventListener";

/** 无caller */
const NONE = Symbol("NONE");

/**
 * 事件管理器
 */
export class EventMgr<EventData = any> {
	private _eventMap = new Map<any, Set<EventListener>>();
	private _callerMap = new Map<any, Map<any, Map<(data: EventListener, id: any) => any, EventListener>>>();

	/**
	 * 是否存在指定监听
	 * @param id 事件id
	 * @param callback 事件回调 不传则忽略判断callback
	 * @param caller 事件回调caller 不传则忽略判断caller
	 */
	has(id: any, callback?: (data: any, e: EventListener) => any, caller?: any) {
		const eSet = this._eventMap.get(id);
		if (eSet) {
			if (!callback && caller === undefined) {
				return true;
			}

			if (callback) {
				for (let e of eSet) {
					if (caller !== undefined && e.caller !== caller) {
						continue;
					}

					if (e.callback === callback) {
						return true;
					}
				}
			}

			if (caller !== undefined) {
				for (let e of eSet) {
					if (e.caller === caller) {
						return true;
					}
				}
			}
		}

		return false;
	}

	/**
	 * 监听事件
	 * @param id 事件id
	 * @param callback 事件回调
	 * @param caller 事件回调caller
	 * @param once 是否在触发回调后取消监听
	 */
	on<EventId extends keyof EventData>(id: EventId, callback: (data: EventData[EventId], e: EventListener) => any, caller?: any, once?: boolean): EventListener;
	on(id: any, callback: (data: any, e: EventListener) => any, caller?: any, once?: boolean): EventListener;
	on(id: any, callback: (data: any, e: EventListener) => any, caller?: any, once: boolean = false): EventListener {
		caller = caller ?? NONE;

		let eIdMap = this._callerMap.get(caller);
		if (!eIdMap) {
			this._callerMap.set(caller, (eIdMap = new Map()));
		}

		let eMap = eIdMap.get(id);
		if (!eMap) {
			eIdMap.set(id, (eMap = new Map()));
		}

		let e = eMap.get(callback);
		if (e) {
			return e;
		}

		let eSet = this._eventMap.get(id);
		if (!eSet) {
			this._eventMap.set(id, (eSet = new Set()));
		}

		e = new EventListener(this, id, callback, caller, once);
		eMap.set(callback, e);
		eSet.add(e);

		return e;
	}

	/**
	 * 监听事件 触发回调后取消监听
	 * @param id 事件id
	 * @param callback 事件回调
	 * @param caller 事件回调caller
	 */
	once<EventId extends keyof EventData>(id: EventId, callback: (data: EventData[EventId], e: EventListener) => any, caller?: any): void;
	once(id: any, callback: (data: any, e: EventListener) => any, caller?: any): void;
	once(id: any, callback: (data: any, e: EventListener) => any, caller?: any): void {
		this.on(id, callback, caller, true);
	}

	/**
	 * 取消监听事件
	 * @param id 事件id
	 * @param callback 事件回调
	 * @param caller 事件回调caller
	 */
	off(id: any, callback: () => any, caller?: any): void {
		caller = caller ?? NONE;

		const eIdMap = this._callerMap.get(caller);
		if (eIdMap) {
			const eMap = eIdMap.get(id);
			if (eMap) {
				const e = eMap.get(callback);
				if (e) {
					eMap.delete(callback);
					if (eMap.size === 0) {
						if (eIdMap.delete(id)) {
							if (eIdMap.size === 0) {
								this._callerMap.delete(caller);
							}
						}
					}
					const eSet = this._eventMap.get(id);
					if (eSet.delete(e)) {
						if (eSet.size === 0) {
							this._eventMap.delete(id);
						}
					}
				}
			}
		}
	}

	/**
	 * 取消事件id的所有监听
	 * @param id
	 */
	offId(id: any) {
		const eSet = this._eventMap.get(id);
		if (eSet) {
			eSet.forEach(e => {
				const eIdMap = this._callerMap.get(e.caller);
				if (eIdMap.delete(id)) {
					if (eIdMap.size === 0) {
						this._callerMap.delete(e.caller);
					}
				}
			});
			eSet.delete(id);
		}
	}

	/**
	 * 取消caller的所有监听
	 * @param caller
	 */
	offCaller(caller: any) {
		caller = caller ?? NONE;

		const eIdMap = this._callerMap.get(caller);
		if (eIdMap) {
			eIdMap.forEach(eMap => {
				eMap.forEach(e => {
					const eSet = this._eventMap.get(e.id);
					if (eSet.delete(e)) {
						if (eSet.size === 0) {
							this._eventMap.delete(e.id);
						}
					}
				});
			});
			this._callerMap.delete(caller);
		}
	}

	/**
	 * 取消listener监听
	 * @param e
	 */
	offListener(e: EventListener) {
		if (e.mgr !== this) return;
		const eIdMap = this._callerMap.get(e.caller);
		const eMap = eIdMap.get(e.id);
		if (eMap) {
			if (eMap.delete(e.callback)) {
				if (eMap.size === 0) {
					if (eIdMap.delete(e.id)) {
						if (eIdMap.size === 0) {
							this._callerMap.delete(e.caller);
						}
					}
				}
				const eSet = this._eventMap.get(e.id);
				if (eSet.delete(e)) {
					if (eSet.size === 0) {
						this._eventMap.delete(e.id);
					}
				}
			}
		}
	}

	/**
	 * 取消所有事件的监听
	 */
	offAll() {
		this._eventMap.clear();
		this._callerMap.clear();
	}

	/**
	 * 发送事件
	 * @param id
	 * @param data
	 */
	send<EventId extends keyof EventData>(id: EventId, data: EventData[EventId]): void;
	send<T>(id: Exclude<T, keyof EventData>, data?: any): void;
	send(id: any, data?: any): void {
		this._eventMap.get(id)?.forEach(e => e.trigger(data));
	}
}
